package com.atguigu.gmall.mq;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.autoconfigure.amqp.RabbitTemplateConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;

/**
 * RabbitMQ的配置
 * 1、RabbitMQ的自动配置
 * 1）、RabbitProperties 封装所有可以配置属性
 * 2)、RabbitTemplateConfigurer: 自定义修改 RabbitTemplate
 * 3）、RabbitTemplate: RedisTemplate,ElasticSearchTemplate 操作客户端
 * 4）、AmqpAdmin:管理RabbitMQ,crud  queue exchange
 *
 *
 */
@EnableRabbit
@Configuration
public class AppRabbitConfig {

    //把要给rabbitmq中放的队列，交换机、绑定关系，通过@Bean放在容器中，也会自动创建

    /**
     * 订单的交换机
     *
     * String name, 交换机名字
     * boolean durable, 持久化
     * boolean autoDelete,  是否自动删除
     * Map<String, Object> arguments  交换机的参数设置
     * @return
     */
    @Bean
    public Exchange orderEventExange(){
        TopicExchange topicExchange = new TopicExchange(RabbitConst.ORDER_EXCHANGE,true,false);
        return topicExchange;
    }

    /**
     * 延时队列
     *
     * String name, 队列的名字
     * boolean durable, 持久化
     * boolean exclusive, 排他的
     * boolean autoDelete 自动删除
     * @Nullable Map<String, Object> arguments
     * x-message-ttl: 60000
     * @return
     */
    @Bean
    public Queue orderDelayQueue(){
        HashMap<String, Object> arguments = new HashMap<>();
        arguments.put("x-dead-letter-exchange",RabbitConst.ORDER_EXCHANGE);
        arguments.put("x-dead-letter-routing-key","order.release.order");
        arguments.put("x-message-ttl",60000);//一分钟过期
        return new Queue(RabbitConst.ORDER_DELAY_QUEUE,true,false,false,arguments);
    }

    /**
     * 死信队列
     * @return
     */
    @Bean
    public Queue orderReleaseOrderQueue(){
        return new Queue(RabbitConst.ORDER_RELEASE_QUEUE,true, false,false);
    }

    /**
     * 绑定关系(交换机和延时队列绑定)
     *
     * String destination, 目的地
     * DestinationType destinationType, 目的地类型
     * String exchange,  交换机
     * String routingKey,  路由键
     * @Nullable Map<String, Object> arguments  参数设置项
     * @return
     */
    @Bean
    public Binding orderCreateOrderBinding(){

        return new Binding(RabbitConst.ORDER_DELAY_QUEUE,
                Binding.DestinationType.QUEUE,
                RabbitConst.ORDER_EXCHANGE,
                RabbitConst.CREATE_ORDER_ROUTKEY,
                null);
    }

    /**
     * 交换机和死信队列的绑定关系
     * @return
     */
    @Bean
    public Binding releaseOrderBinding(){
        return new Binding(RabbitConst.ORDER_RELEASE_QUEUE,
                Binding.DestinationType.QUEUE,
                RabbitConst.ORDER_EXCHANGE,
                "order.release.order",
                null);
    }



    /**
     * 1、手动确认模式
     *
     */
    void test(){
        //rabbitTemplate.setConfirmCallback();//消息只要服务器收到了
        //rabbitTemplate.setReturnCallback();//消息在队列存起来了
    }

    @Bean
    public RabbitTemplate rabbitTemplate(RabbitTemplateConfigurer configurer, ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate();
        configurer.configure(template, connectionFactory);
        /**
         * CorrelationData correlationData 消息的联合数据，消息的主键
         * boolean ack 服务返回true代表消息服务器已经收到
         * @Nullable String cause
         */
        template.setConfirmCallback((correlationData,ack,cause)->{
            if (ack){
                System.out.println("服务器收到了消息");
            }else {
                System.out.println("服务器没有收到消息...."+ correlationData.getReturnedMessage());
                //TODO 存数据库重试，在发送重试
            }
        });
        //消息存到队列里
        //Message message,int replyCode,String replyTest,String exchange,String routingKey
        template.setReturnCallback((message,replyCode,replyText,exchange,routingKey)->{
//消息服务器没有处理成功
            System.out.println(message+"：投递到队列失败");
            System.out.println("响应状态码："+replyCode);
            System.out.println("响应文本："+replyText);
            System.out.println("服务器收到的消息："+message);
            System.out.println("消息交换机："+exchange);
            System.out.println("消息路由件键："+routingKey);
        });

        return template;
    }
}
